From: Florian Müllner Date: Mon, 6 Aug 2018 02:15:12 +0000 (+0200) Subject: extensionPrefs: Improve error UI X-Git-Tag: archive/raspbian/50.5-1+rpi1~1^2~897^2^2~68 X-Git-Url: https://dgit.raspbian.org/%22http://www.example.com/cgi/%22/%22http:/www.example.com/cgi/%22?a=commitdiff_plain;h=43a19739aba6d5e9dfb747dda423d64ab8f230c1;p=gnome-shell.git extensionPrefs: Improve error UI Currently when a preference widget fails to load, we throw a raw backtrace at the user. While that is undoubtedly useful information for extension developers and bug reports, it is gibberish to most users and hardly the first thing they should be exposed to. https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/193 --- diff --git a/js/extensionPrefs/main.js b/js/extensionPrefs/main.js index 2c92bac24..cb7d9edd6 100644 --- a/js/extensionPrefs/main.js +++ b/js/extensionPrefs/main.js @@ -104,29 +104,111 @@ var Application = class { } _buildErrorUI(extension, exc) { - let box = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL }); + let scroll = new Gtk.ScrolledWindow({ + hscrollbar_policy: Gtk.PolicyType.NEVER, + propagate_natural_height: true + }); + + let box = new Gtk.Box({ + orientation: Gtk.Orientation.VERTICAL, + spacing: 12, + margin: 100, + margin_bottom: 60 + }); + scroll.add(box); + let label = new Gtk.Label({ - label: _("There was an error loading the preferences dialog for %s:").format(extension.metadata.name) + label: `${_("Something’s gone wrong")}`, + use_markup: true + }); + label.get_style_context().add_class(Gtk.STYLE_CLASS_DIM_LABEL); + box.add(label); + + label = new Gtk.Label({ + label: _("We’re very sorry, but there’s been a problem: the settings for this extension can’t be displayed. We recommend that you report the issue to the extension authors."), + justify: Gtk.Justification.CENTER, + wrap: true }); box.add(label); - let errortext = ''; - errortext += exc; - errortext += '\n\n'; - errortext += 'Stack trace:\n'; + let expander = new Expander({ + label: _("Technical Details"), + margin_top: 12 + }); + box.add(expander); - // Indent stack trace. - errortext += exc.stack.split('\n').map(line => ' ' + line).join('\n'); + let errortext = `${exc}\n\nStack trace:\n${ + // Indent stack trace. + exc.stack.split('\n').map(line => ` ${line}`).join('\n') + }`; - let scroll = new Gtk.ScrolledWindow({ vexpand: true }); let buffer = new Gtk.TextBuffer({ text: errortext }); - let textview = new Gtk.TextView({ buffer: buffer }); - textview.override_font(Pango.font_description_from_string('monospace')); - scroll.add(textview); - box.add(scroll); + let textview = new Gtk.TextView({ + buffer: buffer, + wrap_mode: Gtk.WrapMode.WORD, + monospace: true, + editable: false, + top_margin: 12, + bottom_margin: 12, + left_margin: 12, + right_margin: 12 + }); + + let toolbar = new Gtk.Toolbar(); + let provider = new Gtk.CssProvider(); + provider.load_from_data(`* { + border: 0 solid @borders; + border-top-width: 1px; + }`); + toolbar.get_style_context().add_provider( + provider, + Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION + ); + + let copyButton = new Gtk.ToolButton({ + icon_name: 'edit-copy-symbolic', + tooltip_text: _("Copy Error") + }); + toolbar.add(copyButton); + + copyButton.connect('clicked', w => { + let clipboard = Gtk.Clipboard.get_default(w.get_display()); + let backticks = '```'; + clipboard.set_text( + // markdown for pasting in gitlab issues + `The settings of extension ${extension.uuid} had an error:\n${ + backticks}\n${exc}\n${backticks}\n\nStack trace:\n${ + backticks}\n${exc.stack}${backticks}\n`, -1 + ); + }); + + let spacing = new Gtk.SeparatorToolItem({ draw: false }); + toolbar.add(spacing); + toolbar.child_set_property(spacing, "expand", true); + + let urlButton = new Gtk.ToolButton({ + label: _("Homepage"), + tooltip_text: _("Visit extension homepage"), + no_show_all: true, + visible: extension.metadata.url != null + }); + toolbar.add(urlButton); + + urlButton.connect('clicked', w => { + let context = w.get_display().get_app_launch_context(); + Gio.AppInfo.launch_default_for_uri(extension.metadata.url, context); + }); + + let expandedBox = new Gtk.Box({ + orientation: Gtk.Orientation.VERTICAL + }); + expandedBox.add(textview); + expandedBox.add(toolbar); + + expander.add(expandedBox); - box.show_all(); - return box; + scroll.show_all(); + return scroll; } _buildUI(app) { @@ -250,6 +332,105 @@ var Application = class { } }; +var Expander = GObject.registerClass({ + Properties: { + 'label': GObject.ParamSpec.string( + 'label', 'label', 'label', + GObject.ParamFlags.READWRITE, + null + ) + } +}, class Expander extends Gtk.Box { + _init(params = {}) { + this._labelText = null; + + super._init(Object.assign(params, { + orientation: Gtk.Orientation.VERTICAL, + spacing: 0 + })); + + this._frame = new Gtk.Frame({ + shadow_type: Gtk.ShadowType.IN, + hexpand: true + }); + + let eventBox = new Gtk.EventBox(); + this._frame.add(eventBox); + + let hbox = new Gtk.Box({ + spacing: 6, + margin: 12 + }); + eventBox.add(hbox); + + this._arrow = new Gtk.Image({ + icon_name: 'pan-end-symbolic' + }); + hbox.add(this._arrow); + + this._label = new Gtk.Label({ label: this._labelText }); + hbox.add(this._label); + + this._revealer = new Gtk.Revealer(); + + this._childBin = new Gtk.Frame({ + shadow_type: Gtk.ShadowType.IN + }); + this._revealer.add(this._childBin); + + // Directly chain up to parent for internal children + super.add(this._frame); + super.add(this._revealer); + + let provider = new Gtk.CssProvider(); + provider.load_from_data('* { border-top-width: 0; }'); + this._childBin.get_style_context().add_provider( + provider, + Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION + ); + + this._gesture = new Gtk.GestureMultiPress({ + widget: this._frame, + button: 0, + exclusive: true + }); + this._gesture.connect('released', (gesture, nPress) => { + if (nPress == 1) + this._revealer.reveal_child = !this._revealer.reveal_child; + }); + this._revealer.connect('notify::reveal-child', () => { + if (this._revealer.reveal_child) + this._arrow.icon_name = 'pan-down-symbolic'; + else + this._arrow.icon_name = 'pan-end-symbolic'; + }); + } + + get label() { + return this._labelText; + } + + set label(text) { + if (this._labelText == text) + return; + + if (this._label) + this._label.label = text; + this._labelText = text; + this.notify('label'); + } + + add(child) { + // set expanded child + this._childBin.get_children().forEach(c => { + this._childBin.remove(c); + }); + + if (child) + this._childBin.add(child); + } +}); + var EmptyPlaceholder = GObject.registerClass( class EmptyPlaceholder extends Gtk.Box { _init() {